package com.rem.chat.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.rem.chat.common.config.AppConfig;
import com.rem.chat.common.constants.Constants;
import com.rem.chat.common.errcode.ServiceErrorCodeConstants;
import com.rem.chat.common.exception.ServiceException;
import com.rem.chat.common.utils.BeanTransUtil;
import com.rem.chat.common.utils.RedisComponent;
import com.rem.chat.common.utils.StringUtil;
import com.rem.chat.mapper.*;
import com.rem.chat.pojo.dataobject.*;
import com.rem.chat.pojo.request.AddOrRemGroupUserRequest;
import com.rem.chat.pojo.response.GetGroupInfo4ChatResponse;
import com.rem.chat.pojo.response.GetGroupInfoResponse;
import com.rem.chat.pojo.response.LoadMyGroupResponse;
import com.rem.chat.pojo.response.SystemSettingResponse;
import com.rem.chat.service.ContactService;
import com.rem.chat.service.GroupService;
import com.rem.chat.service.dto.*;
import com.rem.chat.service.enums.*;
import com.rem.chat.websocket.ChannelContextUtil;
import com.rem.chat.websocket.MessageHandler;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.management.ObjectName;
import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * @author: Themberfue
 * @date: 2025/6/5 21:44
 * @description:
 */
@Service
public class GroupServiceImpl implements GroupService {

    @Resource(name = "contactServiceImpl")
    ContactService contactService;

    @Resource
    GroupInfoMapper groupInfoMapper;

    @Resource
    UserContactMapper userContactMapper;

    @Resource
    RedisComponent redisComponent;

    @Resource
    ChatSessionMapper chatSessionMapper;

    @Resource
    ChatSessionUserMapper chatSessionUserMapper;

    @Resource
    ChatMessageMapper chatMessageMapper;

    @Resource
    UserInfoMapper userInfoMapper;

    @Resource
    ChannelContextUtil channelContextUtil;

    @Resource
    MessageHandler messageHandler;

    @Resource
    AppConfig appConfig;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveGroup(GroupInfoDO groupInfoDO, MultipartFile avatarFile, MultipartFile avatarCover) throws IOException {
        // Step 1. 校验创建群组还是更新群组
        if (!StringUtils.hasLength(groupInfoDO.getGroupId())) { // ? 创建群组
            // Step 1. 校验该用户创建的群组数量是否达到上限
            QueryWrapper<GroupInfoDO> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda()
                        .eq(GroupInfoDO::getGroupOwnerId, groupInfoDO.getGroupOwnerId());
            Long count = groupInfoMapper.selectCount(queryWrapper);
            SystemSettingResponse systemSetting = redisComponent.getSystemSetting();
            if (count >= systemSetting.getMaxGroupCount()) {
                throw new ServiceException(ServiceErrorCodeConstants.GROUP_ADD_GET_MAX_CNT);
            }

            // Step 2. 校验群组头像是否上传
            if (null == avatarFile) {
                throw new ServiceException(ServiceErrorCodeConstants.GROUP_AVATAR_NOT_EXISTS);
            }

            // Step 3. 将群组信息添加到数据库
            groupInfoDO.setGroupId(StringUtil.getGroupId());
            groupInfoMapper.insert(groupInfoDO);

            // Step 4. 将群组添加为联系人（一个群组视为该用户的一个联系人）
            UserContactDO userContactDO = UserContactDO.builder()
                    .userId(groupInfoDO.getGroupOwnerId())
                    .contactId(groupInfoDO.getGroupId())
                    .contactType(UserContactTypeEnum.GROUP.getType())
                    .status(UserContactStatusEnum.FRIEND.getStatus())
                    .build();
            userContactMapper.insert(userContactDO);

            // Step 5. 创建会话
            String sessionId = StringUtil.getSessionId(new String[]{groupInfoDO.getGroupId()});
            ChatSessionDO chatSessionDO = ChatSessionDO.builder()
                    .sessionId(sessionId)
                    .lastMessage(MessageTypeEnum.GROUP_CREATE.getInitMessage())
                    .lastReceiveTime(System.currentTimeMillis())
                    .build();
            chatSessionMapper.insertOrUpdate(chatSessionDO);

            ChatSessionUserDO chatSessionUserDO = ChatSessionUserDO.builder()
                    .sessionId(sessionId)
                    .contactId(groupInfoDO.getGroupId())
                    .contactName(groupInfoDO.getGroupName())
                    .userId(groupInfoDO.getGroupOwnerId())
                    .build();
            chatSessionUserMapper.insert(chatSessionUserDO);

            // Step 6. 创建消息
            ChatMessageDO chatMessageDO = ChatMessageDO.builder()
                    .sessionId(sessionId)
                    .messageType(MessageTypeEnum.GROUP_CREATE.getType())
                    .messageContent(MessageTypeEnum.GROUP_CREATE.getInitMessage())
                    .contactId(groupInfoDO.getGroupId())
                    .contactType(UserContactTypeEnum.GROUP.getType())
                    .status(MessageStatusEnum.SENT.getStatus())
                    .build();
            chatMessageMapper.insert(chatMessageDO);

            // Step 7. 将群组添加到联系人
            redisComponent.saveUserContact(groupInfoDO.getGroupOwnerId(), groupInfoDO.getGroupId());

            // Step 8. 将用户通道添加到群组通道
            channelContextUtil.addUser2Group(groupInfoDO.getGroupId(), groupInfoDO.getGroupOwnerId());

            // Step 9. 发送ws消息
            UserSessionChatDTO userSessionChatDTO = BeanUtil.copyProperties(chatSessionUserDO, UserSessionChatDTO.class);
            userSessionChatDTO.setLastMessage(MessageTypeEnum.ADD_GROUP.getInitMessage());
            userSessionChatDTO.setLastReceiveTime(System.currentTimeMillis());
            userSessionChatDTO.setMemberCount(1L);

            MessageSendDTO<Object> messageSendDTO = BeanUtil.copyProperties(chatMessageDO, MessageSendDTO.class);
            messageSendDTO.setExtendData(userSessionChatDTO);
            messageSendDTO.setLastMessage(MessageTypeEnum.ADD_GROUP.getInitMessage());

            messageHandler.sendMessage(messageSendDTO);
        } else { // ? 更新群组
            // Step 1. 校验是否是群主更新该群组
            GroupInfoDO groupInfo = groupInfoMapper.selectById(groupInfoDO.getGroupId());
            if (!groupInfo.getGroupOwnerId()
                    .equals(groupInfoDO.getGroupOwnerId())) {
                throw new ServiceException(ServiceErrorCodeConstants.GROUP_UPDATE_NOT_OWNER);
            }

            // Step 2. 将群组信息更新到数据库
            groupInfoMapper.updateById(groupInfoDO);

            // Step 3. 更新会话中的群昵称
            String beforeGroupName = groupInfo.getGroupName(), updateGroupName = groupInfoDO.getGroupName();
            if (!beforeGroupName.equals(updateGroupName)) {
                UpdateWrapper<ChatSessionUserDO> updateWrapper = new UpdateWrapper<>();
                updateWrapper.lambda()
                        .eq(ChatSessionUserDO::getContactId, groupInfo.getGroupId())
                        .set(ChatSessionUserDO::getContactName, updateGroupName);
                chatSessionUserMapper.update(updateWrapper);

                // Step 4. 发送更新消息
                MessageSendDTO<Object> messageSendDTO = MessageSendDTO.builder()
                        .contactType(UserContactTypeEnum.GROUP.getType())
                        .contactId(groupInfo.getGroupId())
                        .messageType(MessageTypeEnum.CONTACT_NAME_UPDATE.getType())
                        .extendData(updateGroupName)
                        .build();
                messageHandler.sendMessage(messageSendDTO);
            }
        }

        // Step 2. 校验群组头像是否上传
        if (null == avatarFile) {
            throw new ServiceException(ServiceErrorCodeConstants.GROUP_AVATAR_NOT_EXISTS);
        }

        // Step 3. 将群组头像保存至服务器
        File targetFileFolder = new File(appConfig.getProjectFolder() +
                Constants.FILE_FOLDER_AVATAR);
        if (!targetFileFolder.exists()) {
            boolean isMkdir = targetFileFolder.mkdirs();
            if (!isMkdir) return false;
        }
        String avatarFilePath = targetFileFolder.getPath() + "/" + groupInfoDO.getGroupId() + Constants.IMAGE_SUFFIX;
        String avatarCoverPath = targetFileFolder.getPath() + "/" + groupInfoDO.getGroupId() + Constants.COVER_IMAGE_SUFFIX;
        avatarFile.transferTo(new File(avatarFilePath));
        avatarCover.transferTo(new File(avatarCoverPath));

        return true;
    }

    @Override
    public List<LoadMyGroupResponse> loadMyGroup(String userId) {
        // Step 1. 从数据库中查询该用户的群组
        QueryWrapper<GroupInfoDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(GroupInfoDO::getGroupOwnerId, userId);
        List<GroupInfoDO> groupInfoDOS = groupInfoMapper.selectList(queryWrapper);

        return groupInfoDOS.stream()
                .map(BeanTransUtil::transToLoadGroupInfoResponse)
                .toList();
    }

    @Override
    public GetGroupInfoResponse getGroupInfo(UserLoginDTO user, String groupId) {
        // Step 1. 查询该用户和该群组之间的状态
        QueryWrapper<UserContactDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(UserContactDO::getUserId, user.getUserId())
                .eq(UserContactDO::getContactId, groupId);
        UserContactDO userContactDO = userContactMapper.selectOne(queryWrapper);
        if (null == userContactDO ||
                !UserContactStatusEnum.FRIEND.getStatus()
                        .equals(userContactDO.getStatus())) {
            throw new ServiceException(ServiceErrorCodeConstants.GROUP_WRONG_STATUS);
        }

        // Step 2. 查询群组的状态
        GroupInfoDO groupInfoDO = groupInfoMapper.selectById(groupId);
        if (null == groupInfoDO ||
                GroupStatusEnum.DISSOLUTION.getStatus()
                        .equals(groupInfoDO.getStatus())) {
            throw new ServiceException(ServiceErrorCodeConstants.GROUP_WRONG_STATUS);
        }

        // Step 3. 查询该群组的群员数量
        QueryWrapper<UserContactDO> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.lambda()
                    .eq(UserContactDO::getContactId, groupInfoDO.getGroupId());
        Long count = userContactMapper.selectCount(queryWrapper1);

        return BeanTransUtil.transToGetGroupInfoResponse(groupInfoDO, user.getNickName(), count);
    }

    @Override
    public GetGroupInfo4ChatResponse getGroupInfo4Chat(String groupId) {
        // Step 1. 查询群组信息
        GroupInfoDO groupInfoDO = groupInfoMapper.selectById(groupId);

        // Step 2. 查询该群组的成员
        QueryWrapper<UserContactDO> wrapper = new QueryWrapper<>();
        wrapper.eq("c.contact_id", groupId)
                .eq("c.status", UserContactStatusEnum.FRIEND.getStatus());
        List<ContactInfoDTO> contactInfoDTOList = userContactMapper.selectContactInfo(wrapper);

        return BeanTransUtil.transToGetGroupInfo4ChatResponse(groupInfoDO, contactInfoDTOList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addGroup(String userId, AddOrRemGroupUserRequest param) {
        GroupInfoDO groupInfoDO = validateGroup(userId, param.getGroupId());

        String[] contacts = param.getSelectContacts().split(",");
        for (String contactId: contacts) {
            UserInfoDO userInfoDO = userInfoMapper.selectById(contactId);
            if (null == userInfoDO) {
                throw new ServiceException(ServiceErrorCodeConstants.USER_IS_NOT_EXISTS);
            }
            AddContactDTO addContactDTO = AddContactDTO.builder()
                            .applyUserId(contactId)
                            .contactId(param.getGroupId())
                            .contactType(UserContactTypeEnum.GROUP.getType())
                            .applyInfo(String.format(MessageTypeEnum.ADD_GROUP.getInitMessage(), userInfoDO.getNickName()))
                            .build();
            contactService.addContact(addContactDTO);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean leaveGroup(String userId, AddOrRemGroupUserRequest param, MessageTypeEnum messageTypeEnum) {
        GroupInfoDO groupInfoDO = validateGroup(userId, param.getGroupId());

        String[] contacts = param.getSelectContacts().split(",");
        for (String contactId: contacts) {
            UserInfoDO userInfoDO = userInfoMapper.selectById(contactId);
            if (null == userInfoDO) {
                throw new ServiceException(ServiceErrorCodeConstants.USER_IS_NOT_EXISTS);
            }
            leaveGroup(contactId, param.getGroupId(), messageTypeEnum);
        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean leaveGroup(String userId, String groupId, MessageTypeEnum messageTypeEnum) {
        GroupInfoDO groupInfoDO = validateGroup(userId, groupId);

        QueryWrapper<UserContactDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(UserContactDO::getUserId, userId)
                .eq(UserContactDO::getContactId, groupId);
        userContactMapper.delete(queryWrapper);

        UserInfoDO userInfoDO = userInfoMapper.selectById(userId);

        String sessionId = StringUtil.getSessionId(new String[]{groupId});
        String messageContent = String.format(messageTypeEnum.getInitMessage(), userInfoDO.getNickName());

        ChatSessionDO chatSessionDO = ChatSessionDO.builder()
                .sessionId(sessionId)
                .lastMessage(messageContent)
                .lastReceiveTime(System.currentTimeMillis())
                .build();
        chatSessionMapper.insertOrUpdate(chatSessionDO);

        ChatMessageDO chatMessageDO = ChatMessageDO.builder()
                .sessionId(sessionId)
                .messageContent(messageContent)
                .messageType(messageTypeEnum.getType())
                .contactId(groupId)
                .contactType(UserContactTypeEnum.GROUP.getType())
                .status(MessageStatusEnum.SENT.getStatus())
                .build();
        chatMessageMapper.insert(chatMessageDO);

        queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                    .eq(UserContactDO::getContactId, groupId)
                    .eq(UserContactDO::getStatus, UserContactStatusEnum.FRIEND.getStatus());
        Long memberCount = userContactMapper.selectCount(queryWrapper);

        MessageSendDTO<Object> messageSendDTO = BeanUtil.copyProperties(chatMessageDO, MessageSendDTO.class);
        messageSendDTO.setExtendData(userId);
        messageSendDTO.setMemberCount(memberCount);

        messageHandler.sendMessage(messageSendDTO);

        return true;
    }

    private GroupInfoDO validateGroup(String userId, String groupId) {
        GroupInfoDO groupInfoDO = groupInfoMapper.selectById(groupId);
        if (null == groupInfoDO) {
            throw new ServiceException(ServiceErrorCodeConstants.GROUP_IS_NOT_EXISTS);
        }
        if (!groupInfoDO.getGroupOwnerId().equals(userId)) {
            throw new ServiceException(ServiceErrorCodeConstants.NOT_USER_OPERATE);
        }
        return groupInfoDO;
    }
}
